<template>
  <article class="page page-asar-reverse container">
    <h4 class="text-center">ASAR逆向辅助</h4>
    <form role="form">
      <div class="form-group row-flex" align="center">
        <div class="row-flex-prefix">ASAR路径</div>
        <div class="row-flex-main"><input class="form-control form-control-sm" type="text" v-model="asarPath" readonly /></div>
        <div class="row-flex-suffix"><x-button size="small" @click="setPath">选择...</x-button></div>
      </div>
      <!-- <div class="form-group row-flex" align="center">
        <div class="row-flex-prefix">备份原件</div>
        <div class="row-flex-main text-left">
          <input type="checkbox" class="form-check-input" v-model="backups">
        </div>
      </div> -->
      <div class="form-group row-flex" align="center">
        <div class="row-flex-prefix">代码注入</div>
        <div class="row-flex-main">
          <select class="form-control" v-model="codeType">
            <option v-for="(v,i) in codeTypeList" :key="i" :value="v.value">{{v.label}}</option>
          </select>
        </div>
      </div>
      <div class="form-group row-flex">
        <div class="row-flex-prefix"></div>
        <div class="row-flex-main">
          <x-button type="primary" block :loading="loadingCount > 0" @click="submit">执行操作</x-button>
        </div>
      </div>
      <div class="form-group row-flex" align="center">
        <div class="row-flex-prefix">执行结果</div>
        <div class="row-flex-main">
          <textarea class="form-control" v-model.trim="result" rows="10" readonly></textarea>
        </div>
      </div>
    </form>
  </article>
</template>

<script>
const path = global.require('path')
const fs = global.require('fs')
const {remote} = global.require('electron')
const {dialog} = remote
const selfWindow= remote.getCurrentWindow()
let asar = global.asar || null
let consoleCode = `
  let BrowserWindow = require('electron').BrowserWindow;
  let timer = null;
  timer = setInterval(() => {
    let windows = BrowserWindow.getAllWindows();
    if (windows.length > 0) {
      windows.forEach(v => {
        v && v.webContents.openDevTools();
      })
      clearInterval(timer);
    }
  }, 5000);
`
let containCode = `
  const { app, BrowserWindow } = require("electron");
  //创建窗口
  function createWindow () {
    let mainWindow = new BrowserWindow({
      title: "测试",
      width: 670,
      height: 420,
      offscreen: true,
      show: true,
      titleBarStyle: "customButtonsOnHover",
      backgroundColor: "#fff",
      acceptFirstMouse: true, //是否允许单击页面来激活窗口
      allowRunningInsecureContent: true,//允许一个 https 页面运行 http url 里的资源
      webPreferences: {
        devTools: true, //是否允许打开调试模式
        webSecurity: false,//禁用安全策略
        allowDisplayingInsecureContent: true,//允许一个使用 https的界面来展示由 http URLs 传过来的资源
        allowRunningInsecureContent: true, //允许一个 https 页面运行 http url 里的资源
        nodeIntegration: true//5.x以上版本，默认无法在渲染进程引入node模块，需要这里设置为true
      }
    });
    mainWindow.loadURL('about:blank');
    // 完成第一次绘制后显示
    mainWindow.on('ready-to-show', () => {
      mainWindow.webContents.openDevTools();
    })
    // 窗口关闭
    mainWindow.on('closed', function () {
        mainWindow = null;
    });
  }
  // 主进程准备好以后创建窗口
  app.on('ready', () => {
      createWindow();
  });
`
export default {
  name: 'asar-reverse',
  data () {
    return {
      asarPath: '',
      backups: true,
      codeType: 'console',
      codeTypeList: [
				{label: '打开控制台', value: 'console'},
				{label: '检测是否打包控制台功能', value: 'contain'},
				{label: '只解压资源包', value: 'extract'},
      ],
      result: '',
      loadingCount: 0
    }
  },
  mounted () {
    asar = this.$sharedObject('asar')
  },
  methods: {
    // 设置asar文件路径
    setPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择文件',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openFile']
      })
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.asarPath = filePaths[0]
      }
    },
    // 注入代码
    injectCode (mainPath) {
      let _this = this
      this.loadingCount++
      this.$nextTick(() => {
        let oldCode = ''
        let newCode = ''
        let errFlag = false
        try {
          oldCode = fs.readFileSync(mainPath, 'utf-8')
          _this.result += '读取入口文件内容...\n'
        } catch (err) {
          errFlag = true
          _this.result += '读取入口文件内容失败...\n'
        }
        this.loadingCount--
        if (errFlag) { return this.$toast('读取入口文件内容失败') }
        this.loadingCount++
        if (this.codeType === 'console') {
          newCode = consoleCode + oldCode
        } else if (this.codeType === 'contain') {
          newCode = containCode
        }
        try {
          fs.writeFileSync(mainPath, newCode)
          _this.result += '代码注入成功...\n'
        } catch (err) {
          _this.result += '代码注入失败...\n'
          _this.$toast('代码注入失败')
        }
        this.loadingCount--
      })
    },
    // 执行操作
    submit () {
      if (!this.asarPath) { return this.$toast('请选择要操作的asar文件') }
      if (!fs.existsSync(this.asarPath)) { return this.$toast('asar文件不存在') }
      let _this = this
      let dirPath = path.dirname(this.asarPath)
      let asarName = path.basename(this.asarPath, '.asar')
      let errFlag = false
      this.result = ''
      this.loadingCount++
      this.result += '开始执行操作...\n'
      this.$nextTick(() => {
        // 备份
        // if (_this.backups) {
        //   let bakPath = path.join(dirPath, `${asarName}_${Date.now()}.asar`)
        //   // fs.copyFileSync(_this.asarPath, bakPath)
        //   fs.writeFileSync(bakPath, fs.readFileSync(_this.asarPath))
        //   _this.result += '已完成文件备份...\n'
        // }
        // 解压
        try {
          let toPath = path.join(dirPath, asarName)
          asar.extractAll(_this.asarPath, toPath)
          _this.result += '已完成文件解压...\n'
        } catch (err) {
          errFlag = true
          _this.result += '文件解压失败...\n'
        }
        _this.loadingCount--
        if (errFlag) { return _this.$toast('文件解压失败') }
        if (_this.codeType === 'extract') { return }
        _this.loadingCount++
        // 获取入口文件路径
        let packagePath = path.join(dirPath, asarName, 'package.json')
        _this.$sharedObject('jstoolsNode').getJson(packagePath).then((res) => {
          let mainPath = path.join(dirPath, asarName, res.main || 'main.js')
          if (!fs.existsSync(mainPath)) {
            errFlag = true
            _this.$toast('未找到入口文件')
            _this.result += '未找到入口文件...\n'
          } else {
            _this.result += '已获取入口文件路径...\n'
            _this.injectCode(mainPath)
          }
        }).catch(() => {
          errFlag = true
          _this.$toast('获取入口文件路径失败')
          _this.result += '获取入口文件路径失败...\n'
        }).finally(() => {
          _this.loadingCount--
        })
      })
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.row-flex-prefix {
  width: 5em;
  text-align: left;
}
textarea {
  resize: none;
}
</style>
